# 目的：将输入文件的所有字母转换为大写字母，然后输出到输出文件
# 
# 处理过程： （1）打开输入文件
#           （2）打开输出文件
#            （3）如果未到达输入文件的尾部
#              a、将部分文件读入内存缓冲区
#              b、读取内存缓冲区的每个字节，如果该字节为小写字母，
#                  就将其转换为大写字母
#              c、将内存缓冲区写入输出文件

.section .data
# 系统调用号
.equ SYS_READ,  0
.equ SYS_WRITE, 1
.equ SYS_OPEN,  2
.equ SYS_CLOSE, 3
.equ SYS_EXIT,  60

.equ O_RDONLY, 00
.equ O_CREAD_WDONLY_TRUNC, 03101

.equ STDIN,  0
.equ STDOUT, 1
.equ STDERR, 2

.equ LINUX_SYSCALL, 0x80

.equ END_OF_FILE, 0

.equ NUMBER_ARGUMENTS, 2

.section .bss

.equ BUFFER_SIZE, 500
.lcomm BUFFER_DATA, BUFFER_SIZE

.section .text

.equ ST_SIZE_RESERVE, 16
.equ ST_FD_IN, -8
.equ ST_FD_OUT, -16

.equ ST_ARGC,    0
.equ ST_ARGV_0,  8
.equ ST_ARGV_1,  16
.equ ST_ARGV_2,  24

msg:
    .string "open file failed\n"

.globl _start
_start:
    cmp      $3, (%rsp)
    jne      open_fd_failed

    mov     %rsp, %rbp
    sub     $ST_SIZE_RESERVE, %rsp # 在栈上为文件描述付分配空间

open_fd_in:
    mov     $SYS_OPEN, %rax
    mov     ST_ARGV_1(%rbp), %rdi
    mov     $O_RDONLY, %rsi
    syscall

    cmp     $0, %rax
    jl      open_fd_failed

store_fd_in:
    mov     %rax, ST_FD_IN(%rbp)

open_fd_out:
    mov     $SYS_OPEN, %rax
    mov     ST_ARGV_2(%rbp), %rdi
    mov     $O_CREAD_WDONLY_TRUNC, %rsi
    mov     $0666, %rdx
    syscall 


store_fd_out:
    mov     %rax, ST_FD_OUT(%rbp)

# 主循环开始
read_loop_begin:
    mov     $SYS_READ, %rax
    mov     ST_FD_IN(%rbp), %rdi
    mov     $BUFFER_DATA, %rsi
    mov     $BUFFER_SIZE, %rdx
    syscall

    cmp     $END_OF_FILE, %rax
    jle     end_loop

continue_read_loop:
    push    $BUFFER_DATA
    push    %rax
    call    convert_to_upper
    pop     %rax
    add     $8, %rsp

    mov     %rax, %rdx
    mov     $SYS_WRITE, %rax
    mov     ST_FD_OUT(%rbp), %rdi
    mov     $BUFFER_DATA, %rsi
    syscall 

    jmp     read_loop_begin

end_loop:
    mov     $SYS_CLOSE, %rax
    mov     ST_FD_OUT(%rbp), %rdi
    syscall

    mov     $SYS_CLOSE, %rax
    mov     ST_FD_IN(%rbp), %rdi
    syscall

    mov     $SYS_EXIT, %rax
    mov     $0, %rdi
    syscall



.equ LOWERCASE_A, 'a'
.equ LOWERCASE_Z, 'z'
.equ UPPER_CONVERSION, 'A'-'a'

.equ ST_BUFFER_LEN,16   # 缓冲区长度
.equ ST_BUFFER, 24      # 实际缓冲区

convert_to_upper:
    push    %rbp
    mov     %rsp, %rbp
    # 设置变量
    mov     ST_BUFFER(%rbp), %rax
    mov     ST_BUFFER_LEN(%rbp), %rbx
    mov     $0, %rdi
    # 如果给定的缓冲区的长度为零即离开
    cmp     $0, %rbx
    jle      end_convert_loop

convert_loop:
    # 获取当前字节
    movb    (%rax, %rdi, 1), %cl
    # 除非该字节在a和z之间，否则读取下一字节
    cmpb    $LOWERCASE_A, %cl
    jl      next_byte
    cmpb    $LOWERCASE_Z, %cl
    jg      next_byte
    # 否则将字节转换成大写字母
    add     $UPPER_CONVERSION, %cl
    movb    %cl, (%rax, %rdi, 1)

next_byte:
    inc     %rdi
    cmp     %rdi, %rbx
    jne     convert_loop

end_convert_loop:
    mov     %rbp, %rsp
    pop     %rbp
    ret

open_fd_failed:
    push    %rax
    mov     $SYS_WRITE, %rax
    mov     $2, %rdi
    mov     $msg, %rsi
    mov     $17, %rdx
    syscall  

    mov     $SYS_EXIT, %rax
    pop     %rdi
    syscall

# to run this file, follow the commands below
# $ as toupper.s -o toupper.o
# $ ld toupper.o -o toupper
# $ ./toupper toupper.s txt
# $ cat txt
